/*
 * CDRSCSI.C Version 1.00 (C)1996 HUDSON SOFT
 * SCSI CD-ROM アクセス関数(CDRSCSI.C)
 */
#include    <system.h>
#include    <scsi.h>
#include    "cdrscsi.h"

/* scsi() で使用する SCSI 構造体の実体
 * execute_scsi() 関数で内容を設定し scsi() に渡します。
 */
static SCSI	sccmd;

/* SCSI 構造体に必要なパラメータを設定し scsi() を呼び出し scsi コマンドを実行
 * します。
 *
 * 入力：
 * int		    id;    CD-ROM（ターゲット）の SCSI ID
 * unsigned char    *cdb;  実行する scsi コマンドブロックへのポインタ
 * void		    *dat;  データ転送時に使用するデータ領域へのポインタ
 * int		    siz;   データ転送バイト数（常に偶数を指定する事）
 * int		    mod;   転送モード（READ_MRAM,READ_KRAM,READ_KRAM_DMA）
 *
 * 出力：
 * int		    -1	   scsi コマンドの実行に失敗した
 * 		    0	   scsi コマンドの実行に成功した
 */
static int	execute_scsi(id,cdb,dat,siz,mod)
int		id;
unsigned char	*cdb;
void		*dat;
int		siz;
int		mod;
{
    sccmd.istat=0;
    sccmd.tstat=0;
    sccmd.unit=(0x01<<id);
    sccmd.mess=0;
    sccmd.data=dat;
    sccmd.count=siz;
    sccmd.block=2048;
    sccmd.mode=mod;
    sccmd.cmd=cdb;
    scsi(&sccmd);

    if ( sccmd.istat != INIT_OK || sccmd.tstat != TRGT_GOOD ) {
	return(-1);
    } else {
	return(0);
    }
}

/* CD-ROM に TEST UNIT READY コマンドを発行します。
 * reset_scsi() を実行した後は、必ず TEST UNIT READY コマンドを実行しなければな
 * りません。
 *
 * 入力：
 * int		    id;    CD-ROM（ターゲット）の SCSI ID
 *
 * 出力：
 * int		    -1	   scsi コマンドの実行に失敗した
 * 		    0	   scsi コマンドの実行に成功した
 */
int	cdrom_unit_ready(id)
int	id;
{
    static unsigned char	cdb_buff[] = {
	0x00,	/* 0 */
	0x00,	/* 1 */
	0x00,	/* 2 */
	0x00,	/* 3 */
	0x00,	/* 4 */
	0x00	/* 5 */
    };

    return ( execute_scsi(id,cdb_buff,NULL,0,0) );
}

/* CD-ROM に READ TOC コマンドを発行します。
 *
 * 入力：
 * int		    id;    CD-ROM（ターゲット）の SCSI ID
 * void		    *dat;  CD の TOC 情報を格納するデータ領域へのポインタ
 *			   （通常は struct CDROM_TOC へのポインタを指定する事）
 * unsigned int	    siz;   CD の TOC 情報を格納するデータ領域のバイト数
 *			   （通常は sizeof(CDROM_TOC)を指定する事）
 * int		    mod;   返される CD-ROM 位置情報の形式の指定
 *			   0 位置情報は論理ブロック形式
 *			   1 位置情報は分秒フレーム形式
 * int		    trk;   TOC 情報を要求するトラック
 *
 * 出力：
 * int		    0	   scsi コマンドの実行に成功した
 */
int	cdrom_toc(id,dat,siz,mod,trk)
int		id;
void		*dat;
unsigned int	siz;
int		mod;
int		trk;
{
    static unsigned char	cdb_buff[] = {
	0x43,	/* 0 */
	0x00,	/* 1 */
	0x00,	/* 2 */
	0x00,	/* 3 */
	0x00,	/* 4 */
	0x00,	/* 5 */
	0x00,	/* 6 */
	0x00,	/* 7 */
	0x00,	/* 8 */
	0x00	/* 9 */
    };
    int	    res;

    cdb_buff[1] = 0x00 ;
    if ( mod != 0 ) {
	cdb_buff[1] = 0x02 ;
    }
    cdb_buff[6] = trk ;
    cdb_buff[7] = (siz>>8) ;
    cdb_buff[8] = siz ;

    while ( execute_scsi(id,cdb_buff,dat,siz,READ_MRAM) != 0 )
	;

    return( 0 ) ;
}

/* CD-ROM に READ TOC コマンドを発行し、先頭トラック番号と最終トラック番号を取
 * 得します。
 *
 * 入力：
 * int		    id;    CD-ROM（ターゲット）の SCSI ID
 * CD_INFO_TNUM	    *dat;  取得したトラック番号情報を格納する構造体へのポインタ
 *			   トラック番号は 1 〜 99 の値で格納されます
 *
 * 出力：
 * int		    0	   scsi コマンドの実行に成功した
 */
int	cdrom_info_tnum(id,dat)
int		id;
CD_INFO_TNUM	*dat;
{
    int			res;
    static CDROM_TOC	toc;

    res = cdrom_toc( id, &toc, sizeof(toc), 0, 1 ) ;
    dat->min_track = toc.head.first_track;
    dat->max_track = toc.head.last__track;

    return( res );
}

/* CD-ROM に READ TOC コマンドを発行し、LEAD OUT 位置情報を取得します。
 *
 * 入力：
 * int		    id;    CD-ROM（ターゲット）の SCSI ID
 * CD_INFO_LOUT	    *dat;  取得した LEAD OUT 情報を格納する構造体へのポインタ
 *			   LEAD OUT 情報は分秒フレームで格納されます
 *
 * 出力：
 * int		    0	   scsi コマンドの実行に成功した
 */
int	cdrom_info_lout(id,dat)
int		id;
CD_INFO_LOUT	*dat;
{
    int			res;
    static CDROM_TOC	toc;

    res = cdrom_toc( id, &toc, sizeof(toc), 1, 0xaa ) ;
    dat->leadout_min = toc.data[0].d1;
    dat->leadout_sec = toc.data[0].d2;
    dat->leadout_fra = toc.data[0].d3;

    return( res );
}

/* CD-ROM に READ TOC コマンドを発行し、指定トラックの位置情報（分秒フレーム）
 * を取得します。
 *
 * 入力：
 * int		    id;    CD-ROM（ターゲット）の SCSI ID
 * CD_INFO_TIME	    *dat;  取得した位置情報を格納する構造体へのポインタ
 *			   位置情報は分秒フレームで格納されます
 * int		    trk;   位置情報を要求するトラック
 *
 * 出力：
 * int		    0	   scsi コマンドの実行に成功した
 *		    -1	   指定したトラック番号が無効
 */
int	cdrom_info_time(id,dat,trk)
int		id;
CD_INFO_TIME	*dat;
int		trk;
{
    int			res;
    static CD_INFO_LOUT	lout;
    static CD_INFO_TNUM	tnum;
    static CDROM_TOC	toc;

    dat->typ = -1 ;
    if ( (res=cdrom_info_tnum(id,&tnum)) != 0 ) {
	return(res);
    }
    if ( trk < tnum.min_track ) {
	return(-1);
    } if ( trk > tnum.max_track ) {
	res = cdrom_info_lout( id, &lout ) ;
	dat->min = lout.leadout_min;
	dat->sec = lout.leadout_sec;
	dat->fra = lout.leadout_fra;
    } else {
	res = cdrom_toc( id, &toc, sizeof(toc), 1, trk ) ;
	dat->min = toc.data[0].d1;
	dat->sec = toc.data[0].d2;
	dat->fra = toc.data[0].d3;
	dat->typ = (toc.data[0].adr_control & 0x0f) ;
    }
    return( res );
}

/* CD-ROM に READ TOC コマンドを発行し、指定トラックの位置情報（論理ブロック）
 * を取得します。
 * 論理ブロック位置は、０分２秒０フレームが０になります。
 *
 * 入力：
 * int		    id;    CD-ROM（ターゲット）の SCSI ID
 * CD_INFO_SECT	    *dat;  取得した位置情報を格納する構造体へのポインタ
 *			   位置情報は論理ブロックで格納されます
 * int		    trk;   位置情報を要求するトラック
 *
 * 出力：
 * int		    0	   scsi コマンドの実行に成功した
 *		    -1	   指定したトラック番号が無効
 */
int	cdrom_info_sect(id,dat,trk)
int		id;
CD_INFO_SECT	*dat;
int		trk;
{
    int			res;
    static CD_INFO_TIME time;
    unsigned int	sect;

    res = cdrom_info_time(id,&time,trk);
    sect  = time.min;
    sect *= 60 ;
    sect += time.sec;
    sect *= 75 ;
    sect += time.fra;
    sect -= ( 75 * 2 ) ;
    dat->sect = sect ;
    dat->typ = time.typ;
    return(res);
}

/* CD-ROM に READ EXTENDED コマンドを発行し、指定ブロックから指定したブロック長
 * のデータをメインメモリに読み込みます。
 *
 * 入力：
 * int		    id;    CD-ROM（ターゲット）の SCSI ID
 * unsigned int	    sect;  読み込み開始ブロック位置
 * void		    *buff; 読み込んだデータを格納する領域へのポインタ
 *			   （領域の大きさは読み込みブロック数×2048バイト必要）
 * unsigned int	    ssiz;  読み込みブロック数
 *
 * 出力：
 * int		    0	   scsi コマンドの実行に成功した
 *
 * 注意：
 * エラーが発生した場合は単純に再試行します。従って、オーディオトラックに対する
 * 読み込み動作、読み込み可能範囲外からの読み込みなどのように、必ずエラーが発生
 * する呼び出しを行なうと、無限に再試行を繰り返し復帰できなくなります。不正な呼
 * び出しはしないで下さい。
 */
int	cdrom_read( id, sect, buff, ssiz )
int		id;
unsigned int	sect;
void		*buff;
unsigned int	ssiz;
{
    static unsigned char	cdb_buff[] = {
	0x28,	/* 0 */
	0x00,	/* 1 */
	0x00,	/* 2 */
	0x00,	/* 3 */
	0x00,	/* 4 */
	0x00,	/* 5 */
	0x00,	/* 6 */
	0x00,	/* 7 */
	0x00,	/* 8 */
	0x00	/* 9 */
    };

    cdb_buff[2]=(sect>>24);
    cdb_buff[3]=(sect>>16);
    cdb_buff[4]=(sect>>8);
    cdb_buff[5]=(sect>>0);
    cdb_buff[7]=(ssiz>>8);
    cdb_buff[8]=(ssiz>>0);

    while ( execute_scsi(id,cdb_buff,buff,(ssiz * 2048),READ_MRAM) != 0 )
	;

    return(0);
}

/* CD-ROM に EJECT コマンドを発行し、CD-ROM をイジェクトします。
 *
 * 入力：
 * int		    id;    CD-ROM（ターゲット）の SCSI ID
 *
 * 出力：
 * int		    0	   scsi コマンドの実行に成功した
 *
 * 注意：
 * このサンプルプログラムでは、このコマンドをホストマシン側のプログラムにエラー
 * を伴う動作の終了を通知する目的で使用しています。
 * ホストマシン側のサンプルプログラムでは、このコマンドによってイジェクトを指示
 * されると動作を終了し DOS に戻るように作成されています。
 * 従って、このコマンドを実行した後は、PC-FXGA 側プログラムも動作を終了しなけれ
 * ばなりません。
 */
int	cdrom_eject(id)
int	id;
{
    static unsigned char	cdb_buff[] = {
	0xdc,	/* 0 */
	0x00,	/* 1 */
	0x00,	/* 2 */
	0x00,	/* 3 */
	0x00,	/* 4 */
	0x00,	/* 5 */
	0x00,	/* 6 */
	0x00,	/* 7 */
	0x00,	/* 8 */
	0x00	/* 9 */
    };
    return ( execute_scsi(id,cdb_buff,NULL,0,0) );
}

/* CD-ROM に START/STOP UNIT コマンドを発行し、CD-ROM をイジェクトします。
 *
 * 入力：
 * int		    id;    CD-ROM（ターゲット）の SCSI ID
 *
 * 出力：
 * int		    0	   scsi コマンドの実行に成功した
 *
 * 注意：
 * このサンプルプログラムでは、このコマンドをホストマシン側のプログラムに動作の
 * 終了を通知する目的で使用しています。
 * ホストマシン側のサンプルプログラムでは、このコマンドによってイジェクトを指示
 * されると動作を終了し DOS に戻るように作成されています。
 * 従って、このコマンドを実行した後は、PC-FXGA 側プログラムも動作を終了しなけれ
 * ばなりません。
 */
int	cdrom_stop(id)
int	id;
{
    static unsigned char	cdb_buff[] = {
	0x1b,	/* 0 */
	0x00,	/* 1 */
	0x00,	/* 2 */
	0x00,	/* 3 */
	0x02,	/* 4 */
	0x00	/* 5 */
    };
    return ( execute_scsi(id,cdb_buff,NULL,0,0) );
}
